﻿using System;
using System.Collections.Generic;

using RoughJs.Error;
using System.IO;

namespace RoughJs.Ast
{
    /// <summary>
    /// 2010-01-5 ----- 2010-01-29
    /// 陈曦 1.0
    /// Microsoft Public License (Ms-PL)
    ///This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
    ///1. Definitions
    ///The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
    ///A "contribution" is the original software, or any additions or changes to the software.
    ///A "contributor" is any person that distributes its contribution under this license.
    ///"Licensed patents" are a contributor's patent claims that read directly on its contribution.
    ///2. Grant of Rights
    ///(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
    ///(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
    ///3. Conditions and Limitations
    ///(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
    ///(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
    ///(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
    ///(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
    ///(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
    /// </summary>
    public class Parser
    {
        public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter)
        {
            this.compilerEnv = compilerEnv;
            this.errorReporter = errorReporter;
        }
        /*
     * Build a parse tree from the given sourceString.
     *
     * @return an Object representing the parsed
     * program.  If the parse fails, null will be returned.  (The
     * parse failure will result in a call to the ErrorReporter from
     * CompilerEnvirons.)
     */
        public ScriptOrFnNode parse(StreamReader sourceReader,
                                    String sourceURI, int lineno)
        {
            this.sourceURI = sourceURI;
            this.ts = new TokenStream(this, sourceReader, null, lineno);
            return parse();
        }
        public ScriptOrFnNode parse(String sourceString, String sourceURI, int lineno)
        {
            this.sourceURI = sourceURI;
            //初始化标记流
            this.ts = new TokenStream(this, null, sourceString, lineno);
            try
            {
                return parse();
            }
            catch
            {
                throw new Exception("");
            }
        }
        public ScriptOrFnNode parse()
        {
            this.decompiler = createDecompiler();//创建一个编译器
            this.nf = new IRFactory(this);//加入一个处理器
            currentScriptOrFn = nf.createScript();//创建脚本
            currentScope = currentScriptOrFn;//当前的域
            int sourceStartOffset = decompiler.getCurrentOffset();//得到当前的偏移量
            this.encodedSource = null;
            decompiler.addToken(Token.SCRIPT);//加入树根

            this.currentFlaggedToken = Token.EOF;//当前标记为文件尾
            this.syntaxErrorCount = 0;
            int baseLineno = ts.getLineno();  // 源代码的起始行


            Node pn = nf.createLeaf(Token.BLOCK);//创建叶节点

            try
            {
                for (; ; )
                {
                    int tt = peekToken();//取出第一个标记
                    if (tt <= Token.EOF)
                        break;
                    Node n;
                    if (tt == Token.FUNCTION)
                    {
                        try
                        {
                            consumeToken();
                            n = function(calledByCompileFunction
                                         ? FunctionNode.FUNCTION_EXPRESSION
                                         : FunctionNode.FUNCTION_STATEMENT);
                        }
                        catch (ParserException e)
                        {
                            break;
                        }
                    }
                    else
                    {
                        n = statement();
                    }
                    nf.addChildToBack(pn, n);
                }
            }
            catch (OutOfMemoryException ex)
            {
                String msg = ScriptRuntime.getMessage0(
                "msg.too.deep.parser.recursion");
                throw Context.reportRuntimeError(msg, sourceURI,
                                                 ts.getLineno(), null, 0);
            }

            if (this.syntaxErrorCount != 0)
            {
                String msg = this.syntaxErrorCount.ToString();
                msg = ScriptRuntime.getMessage1("msg.got.syntax.errors", msg);
                throw errorReporter.runtimeError(msg, sourceURI, baseLineno,
                                                 null, 0);
            }
            currentScriptOrFn.setSourceName(sourceURI);
            currentScriptOrFn.setBaseLineno(baseLineno);
            currentScriptOrFn.setEndLineno(ts.getLineno());

            int sourceEndOffset = decompiler.getCurrentOffset();
            currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset,
                                                     sourceEndOffset);

            nf.initScript(currentScriptOrFn, pn);

            if (compilerEnv.isGeneratingSource())
            {
                encodedSource = decompiler.getEncodedSource();
            }
            this.decompiler = null; // It helps GC

            return currentScriptOrFn;
        }
        /// <summary>
        /// 如果为语句
        /// </summary>
        /// <returns></returns>
        private Node statement()
        {
            try
            {
                Node pn = statementHelper(null);
                if (pn != null)
                {
                    if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
                        addStrictWarning("msg.no.side.effects", "");
                    return pn;
                }
            }
            catch (ParserException e) { }
            // skip to end of statement
            int lineno = ts.getLineno();
            for (; ; )
            {
                int tt = peekTokenOrEOL();
                consumeToken();
                switch (tt)
                {
                    case Token.ERROR:
                    case Token.EOF:
                    case Token.EOL:
                    case Token.SEMI:
                        goto guessingStatementEnd;
                }
            }
        guessingStatementEnd: ;
            return nf.createExprStatement(nf.createName("error"), lineno);
            //return null;
        }

        // match a NAME; return null if no match.
    private Node matchJumpLabelName()
        
    {
        Node label = null;

        int tt = peekTokenOrEOL();
        if (tt == Token.NAME) {
            consumeToken();
            String name = ts.getString();
            decompiler.addName(name);
            if (labelSet != null) {
                if (labelSet.ContainsKey(name))
                    label = labelSet[name];
                else
                    label = null;
            }
            if (label == null) {
                reportError("msg.undef.label");
            }
        }

        return label;
    }

        /// <summary>
        /// 语句构建帮助
        /// </summary>
        /// <param name="statementLabel"></param>
        /// <returns></returns>
        private Node statementHelper(Node statementLabel)
        {
            Node pn = null;
            int tt = peekToken();
            switch (tt)
            {
                case Token.IF:
                    {
                        consumeToken();

                        decompiler.addToken(Token.IF);
                        int lineno = ts.getLineno();
                        Node cond = condition();
                        decompiler.addEOL(Token.LC);
                        Node ifTrue = statement();
                        Node ifFalse = null;
                        if (matchToken(Token.ELSE))
                        {
                            decompiler.addToken(Token.RC);
                            decompiler.addToken(Token.ELSE);
                            decompiler.addEOL(Token.LC);
                            ifFalse = statement();
                        }
                        decompiler.addEOL(Token.RC);
                        pn = nf.createIf(cond, ifTrue, ifFalse, lineno);
                        return pn;

                    }
                case Token.SWITCH:
                    {
                        consumeToken();

                        decompiler.addToken(Token.SWITCH);
                        int lineno = ts.getLineno();
                        mustMatchToken(Token.LP, "msg.no.paren.switch");
                        decompiler.addToken(Token.LP);
                        pn = enterSwitch(Expr(false), lineno);
                        try
                        {
                            mustMatchToken(Token.RP, "msg.no.paren.after.switch");
                            decompiler.addToken(Token.RP);
                            mustMatchToken(Token.LC, "msg.no.brace.switch");
                            decompiler.addEOL(Token.LC);

                            bool hasDefault = false;
                            for (; ; )
                            {
                                tt = nextToken();
                                Node caseExpression;
                                switch (tt)
                                {
                                    case Token.RC:
                                        goto switchLoop;

                                    case Token.CASE:
                                        decompiler.addToken(Token.CASE);
                                        caseExpression = Expr(false);
                                        mustMatchToken(Token.COLON, "msg.no.colon.case");
                                        decompiler.addEOL(Token.COLON);
                                        break;

                                    case Token.DEFAULT:
                                        if (hasDefault)
                                        {
                                            reportError("msg.double.switch.default");
                                        }
                                        decompiler.addToken(Token.DEFAULT);
                                        hasDefault = true;
                                        caseExpression = null;
                                        mustMatchToken(Token.COLON, "msg.no.colon.case");
                                        decompiler.addEOL(Token.COLON);
                                        break;

                                    default:
                                        reportError("msg.bad.switch");
                                        goto switchLoop;
                                }

                                Node block = nf.createLeaf(Token.BLOCK);
                                while ((tt = peekToken()) != Token.RC
                                       && tt != Token.CASE
                                       && tt != Token.DEFAULT
                                       && tt != Token.EOF)
                                {
                                    nf.addChildToBack(block, statement());
                                }

                                // caseExpression == null => add default label
                                nf.addSwitchCase(pn, caseExpression, block);
                            }
                        switchLoop: ;
                            decompiler.addEOL(Token.RC);
                            nf.closeSwitch(pn);
                        }
                        finally
                        {
                            exitSwitch();
                        }
                        return pn;
                    }
                case Token.WHILE:
                    {
                        consumeToken();
                        decompiler.addToken(Token.WHILE);

                        Node loop = enterLoop(statementLabel, true);
                        try
                        {
                            Node cond = condition();
                            decompiler.addEOL(Token.LC);
                            Node body = statement();
                            decompiler.addEOL(Token.RC);
                            pn = nf.createWhile(loop, cond, body);
                        }
                        finally
                        {
                            exitLoop(true);
                        }
                        return pn;
                    }
                case Token.DO:
                    {
                        consumeToken();
                        decompiler.addToken(Token.DO);
                        decompiler.addEOL(Token.LC);

                        Node loop = enterLoop(statementLabel, true);
                        try
                        {
                            Node body = statement();
                            decompiler.addToken(Token.RC);
                            mustMatchToken(Token.WHILE, "msg.no.while.do");
                            decompiler.addToken(Token.WHILE);
                            Node cond = condition();
                            pn = nf.createDoWhile(loop, body, cond);
                        }
                        finally
                        {
                            exitLoop(true);
                        }
                        // Always auto-insert semicolon to follow SpiderMonkey:
                        // It is required by ECMAScript but is ignored by the rest of
                        // world, see bug 238945
                        matchToken(Token.SEMI);
                        decompiler.addEOL(Token.SEMI);
                        return pn;
                    }
                case Token.FOR:
                    {
                        consumeToken();
                        bool isForEach = false;
                        decompiler.addToken(Token.FOR);

                        Node loop = enterLoop(statementLabel, true);
                        try
                        {
                            Node init;  // Node init is also foo in 'foo in object'
                            Node cond;  // Node cond is also object in 'foo in object'
                            Node incr = null;
                            Node body;
                            int declType = -1;

                            // See if this is a for each () instead of just a for ()
                            if (matchToken(Token.NAME))
                            {
                                decompiler.addName(ts.getString());
                                if (ts.getString()=="each")
                                {
                                    isForEach = true;
                                }
                                else
                                {
                                    reportError("msg.no.paren.for");
                                }
                            }

                            mustMatchToken(Token.LP, "msg.no.paren.for");
                            decompiler.addToken(Token.LP);
                            tt = peekToken();
                            if (tt == Token.SEMI)
                            {
                                init = nf.createLeaf(Token.EMPTY);
                            }
                            else
                            {
                                if (tt == Token.VAR || tt == Token.LET)
                                {
                                    // set init to a var list or initial
                                    consumeToken();    // consume the token
                                    decompiler.addToken(tt);
                                    init = variables(true, tt);
                                    declType = tt;
                                }
                                else
                                {
                                    init = Expr(true);
                                }
                            }

                            if (matchToken(Token.IN))
                            {
                                decompiler.addToken(Token.IN);
                                // 'cond' is the object over which we're iterating
                                cond = Expr(false);
                            }
                            else
                            {  // ordinary for loop
                                mustMatchToken(Token.SEMI, "msg.no.semi.for");
                                decompiler.addToken(Token.SEMI);
                                if (peekToken() == Token.SEMI)
                                {
                                    // no loop condition
                                    cond = nf.createLeaf(Token.EMPTY);
                                }
                                else
                                {
                                    cond = Expr(false);
                                }

                                mustMatchToken(Token.SEMI, "msg.no.semi.for.cond");
                                decompiler.addToken(Token.SEMI);
                                if (peekToken() == Token.RP)
                                {
                                    incr = nf.createLeaf(Token.EMPTY);
                                }
                                else
                                {
                                    incr = Expr(false);
                                }
                            }

                            mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
                            decompiler.addToken(Token.RP);
                            decompiler.addEOL(Token.LC);
                            body = statement();
                            decompiler.addEOL(Token.RC);

                            if (incr == null)
                            {
                                // cond could be null if 'in obj' got eaten
                                // by the init node.
                                pn = nf.createForIn(declType, loop, init, cond, body,
                                                    isForEach);
                            }
                            else
                            {
                                pn = nf.createFor(loop, init, cond, incr, body);
                            }
                        }
                        finally
                        {
                            exitLoop(true);
                        }
                        return pn;
                    }
                case Token.TRY:
                    {
                        consumeToken();
                        int lineno = ts.getLineno();

                        Node tryblock;
                        Node catchblocks = null;
                        Node finallyblock = null;

                        decompiler.addToken(Token.TRY);
                        if (peekToken() != Token.LC)
                        {
                            reportError("msg.no.brace.try");
                        }
                        decompiler.addEOL(Token.LC);
                        tryblock = statement();
                        decompiler.addEOL(Token.RC);

                        catchblocks = nf.createLeaf(Token.BLOCK);

                        bool sawDefaultCatch = false;
                        int peek = peekToken();
                        if (peek == Token.CATCH)
                        {
                            while (matchToken(Token.CATCH))
                            {
                                if (sawDefaultCatch)
                                {
                                    reportError("msg.catch.unreachable");
                                }
                                decompiler.addToken(Token.CATCH);
                                mustMatchToken(Token.LP, "msg.no.paren.catch");
                                decompiler.addToken(Token.LP);

                                mustMatchToken(Token.NAME, "msg.bad.catchcond");
                                String varName = ts.getString();
                                decompiler.addName(varName);

                                Node catchCond = null;
                                if (matchToken(Token.IF))
                                {
                                    decompiler.addToken(Token.IF);
                                    catchCond = Expr(false);
                                }
                                else
                                {
                                    sawDefaultCatch = true;
                                }

                                mustMatchToken(Token.RP, "msg.bad.catchcond");
                                decompiler.addToken(Token.RP);
                                mustMatchToken(Token.LC, "msg.no.brace.catchblock");
                                decompiler.addEOL(Token.LC);

                                nf.addChildToBack(catchblocks,
                                    nf.createCatch(varName, catchCond,
                                                   statements(null),
                                                   ts.getLineno()));

                                mustMatchToken(Token.RC, "msg.no.brace.after.body");
                                decompiler.addEOL(Token.RC);
                            }
                        }
                        else if (peek != Token.FINALLY)
                        {
                            mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally");
                        }

                        if (matchToken(Token.FINALLY))
                        {
                            decompiler.addToken(Token.FINALLY);
                            decompiler.addEOL(Token.LC);
                            finallyblock = statement();
                            decompiler.addEOL(Token.RC);
                        }

                        pn = nf.createTryCatchFinally(tryblock, catchblocks,
                                                      finallyblock, lineno);

                        return pn;
                    }

                case Token.THROW:
                    {
                        consumeToken();
                        if (peekTokenOrEOL() == Token.EOL)
                        {
                            // ECMAScript does not allow new lines before throw expression,
                            // see bug 256617
                            reportError("msg.bad.throw.eol");
                        }

                        int lineno = ts.getLineno();
                        decompiler.addToken(Token.THROW);
                        pn = nf.createThrow(Expr(false), lineno);
                        break;
                    }

                case Token.BREAK:
                    {
                        consumeToken();
                        int lineno = ts.getLineno();

                        decompiler.addToken(Token.BREAK);

                        // matchJumpLabelName only matches if there is one
                        Node breakStatement = matchJumpLabelName();
                        if (breakStatement == null)
                        {
                            if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0)
                            {
                                reportError("msg.bad.break");
                                return null;
                            }
                            breakStatement = (Node)loopAndSwitchSet.peek();
                        }
                        pn = nf.createBreak(breakStatement, lineno);
                        break;
                    }

                case Token.CONTINUE:
                    {
                        consumeToken();
                        int lineno = ts.getLineno();

                        decompiler.addToken(Token.CONTINUE);

                        Node loop;
                        // matchJumpLabelName only matches if there is one
                        Node label = matchJumpLabelName();
                        if (label == null)
                        {
                            if (loopSet == null || loopSet.size() == 0)
                            {
                                reportError("msg.continue.outside");
                                return null;
                            }
                            loop = (Node)loopSet.peek();
                        }
                        else
                        {
                            loop = nf.getLabelLoop(label);
                            if (loop == null)
                            {
                                reportError("msg.continue.nonloop");
                                return null;
                            }
                        }
                        pn = nf.createContinue(loop, lineno);
                        break;
                    }
                case Token.WITH:
                    {
                        consumeToken();

                        decompiler.addToken(Token.WITH);
                        int lineno = ts.getLineno();
                        mustMatchToken(Token.LP, "msg.no.paren.with");
                        decompiler.addToken(Token.LP);
                        Node obj = Expr(false);
                        mustMatchToken(Token.RP, "msg.no.paren.after.with");
                        decompiler.addToken(Token.RP);
                        decompiler.addEOL(Token.LC);

                        ++nestingOfWith;
                        Node body;
                        try
                        {
                            body = statement();
                        }
                        finally
                        {
                            --nestingOfWith;
                        }

                        decompiler.addEOL(Token.RC);

                        pn = nf.createWith(obj, body, lineno);
                        return pn;
                    }

                case Token.CONST:
                case Token.VAR:
                    {
                        consumeToken();//销毁当前使用的标记
                        decompiler.addToken(tt);//在编译器中加入一个标记
                        pn = variables(false, tt);//
                        break;
                    }
                case Token.LET:
                    {
                        consumeToken();
                        decompiler.addToken(Token.LET);
                        if (peekToken() == Token.LP)
                        {
                            return let(true);
                        }
                        else
                        {
                            pn = variables(false, tt);
                            if (peekToken() == Token.SEMI)
                                break;
                            return pn;
                        }
                    }


                case Token.RETURN:
                case Token.YIELD:
                    {
                        pn = returnOrYield(tt, false);
                        break;
                    }
                case Token.DEBUGGER:
                    consumeToken();
                    decompiler.addToken(Token.DEBUGGER);
                    pn = nf.createDebugger(ts.getLineno());
                    break;
                case Token.LC:
                    consumeToken();
                    if (statementLabel != null)
                    {
                        decompiler.addToken(Token.LC);
                    }
                    Node scope = nf.createScopeNode(Token.BLOCK, ts.getLineno());
                    pushScope(scope);
                    try
                    {
                        statements(scope);
                        mustMatchToken(Token.RC, "msg.no.brace.block");
                        if (statementLabel != null)
                        {
                            decompiler.addEOL(Token.RC);
                        }
                        return scope;
                    }
                    finally
                    {
                        popScope();
                    }

                case Token.ERROR:
                // Fall thru, to have a node for error recovery to work on
                case Token.SEMI:
                    consumeToken();
                    pn = nf.createLeaf(Token.EMPTY);
                    return pn;

                case Token.FUNCTION:
                    {
                        consumeToken();
                        pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
                        return pn;
                    }

                case Token.DEFAULT:
                    {
                        consumeToken();
                        mustHaveXML();

                        decompiler.addToken(Token.DEFAULT);
                        int nsLine = ts.getLineno();

                        if (!(matchToken(Token.NAME)
                              && ts.getString() == "xml"))
                        {
                            reportError("msg.bad.namespace");
                        }
                        decompiler.addName(" xml");

                        if (!(matchToken(Token.NAME)
                              && ts.getString() == "namespace"))
                        {
                            reportError("msg.bad.namespace");
                        }
                        decompiler.addName(" namespace");

                        if (!matchToken(Token.ASSIGN))
                        {
                            reportError("msg.bad.namespace");
                        }
                        decompiler.addToken(Token.ASSIGN);

                        Node expr = Expr(false);
                        pn = nf.createDefaultNamespace(expr, nsLine);
                        break;
                    }
                case Token.NAME:
                    {
                        int lineno = ts.getLineno();
                        String name = ts.getString();
                        setCheckForLabel();
                        pn = Expr(false);
                        if (pn.getType() != Token.LABEL)
                        {
                            pn = nf.createExprStatement(pn, lineno);
                        }
                        else
                        {
                            // Parsed the label: push back token should be
                            // colon that primaryExpr left untouched.
                            if (peekToken() != Token.COLON) Kit.codeBug();
                            consumeToken();
                            // depend on decompiling lookahead to guess that that
                            // last name was a label.
                            decompiler.addName(name);
                            decompiler.addEOL(Token.COLON);

                            if (labelSet == null)
                            {
                                labelSet = new Dictionary<String, Node>();
                            }
                            else if (labelSet.ContainsKey(name))
                            {
                                reportError("msg.dup.label");
                            }

                            bool firstLabel;
                            if (statementLabel == null)
                            {
                                firstLabel = true;
                                statementLabel = pn;
                            }
                            else
                            {
                                // Discard multiple label nodes and use only
                                // the first: it allows to simplify IRFactory
                                firstLabel = false;
                            }
                            labelSet[name]= statementLabel;
                            try
                            {
                                pn = statementHelper(statementLabel);
                            }
                            finally
                            {
                                labelSet.Remove(name);
                            }
                            if (firstLabel)
                            {
                                pn = nf.createLabeledStatement(statementLabel, pn);
                            }
                            return pn;
                        }
                        break;
                    }

                default:
                    {
                        int lineno = ts.getLineno();
                        pn = Expr(false);
                        pn = nf.createExprStatement(pn, lineno);
                        break;
                    }
            }//for

           
            int ttFlagged = peekFlaggedToken();
            switch (ttFlagged & CLEAR_TI_MASK)
            {
                case Token.SEMI:
                    // 语句结尾 ';'
                    consumeToken();
                    break;
                case Token.ERROR:
                case Token.EOF:
                case Token.RC:
                    // Autoinsert ;
                    break;
                default:
                    if ((ttFlagged & TI_AFTER_EOL) == 0)
                    {
                        // Report error if no EOL or autoinsert ; otherwise
                        reportError("msg.no.semi.stmt");
                    }
                    break;
            }
            decompiler.addEOL(Token.SEMI);
            return pn;
        }
        /// <summary>
        /// 核对标号
        /// </summary>
        private void setCheckForLabel()
        {
            if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME)
                throw Kit.codeBug();
            currentFlaggedToken |= TI_CHECK_LABEL;
        }


        private int peekFlaggedToken()
        {
            peekToken();
            return currentFlaggedToken;
        }

        /// <summary>
        /// 解析一个'var' 或者'const'语句，或者在for语句中初始化一个'var'列表
        /// </summary>
        /// <param name="inFor">如果为true 表示在一个for语句中</param>
        /// <param name="declType">一个标记值:VAR,CONST,或者LET在上下文中</param>
        /// <returns></returns>
        private Node variables(bool inFor, int declType)
        {
            Node result = nf.createVaribles(declType, ts.getLineno());
            bool first = true;
            for (; ; )
            {
                Node destructuring = null;
                String s = null;
                int tt = peekToken();
                if (tt == Token.LB || tt == Token.LC)
                {
                    //非结构化的赋值语 e.g. var [a,b] =...
                    destructuring = primaryExpr();
                }
                else
                {
                    //一个简单变量名
                    mustMatchToken(Token.NAME, "msg.bad.var");//如果不能向前扫描，那就报错
                    s = ts.getString();
                    if (!first)
                        decompiler.addToken(Token.COMMA);
                    first = false;
                    decompiler.addName(s);
                    defineSymbol(declType, inFor, s);
                }
                Node init = null;
                if (matchToken(Token.ASSIGN))
                {
                    decompiler.addToken(Token.ASSIGN);
                    init = assignExpr(inFor);

                }

                if (destructuring != null)
                {
                    if (init == null)
                    {
                        if (!inFor)
                            reportError("msg.destruct.assign.no.init");
                        nf.addChildToBack(result, destructuring);
                    }
                    else
                    {
                        nf.addChildToBack(result,
                            nf.createDestructuringAssignment(declType,
                                destructuring, init));
                    }
                }
                else
                {
                    Node name = nf.createName(s);
                    if (init != null)
                        nf.addChildToBack(name, init);
                    nf.addChildToBack(result, name);
                }

                if (!matchToken(Token.COMMA))
                    break;
            }
            return result;
        }

        private Node enterSwitch(Node switchSelector, int lineno)
        {
            Node switchNode = nf.createSwitch(switchSelector, lineno);
            if (loopAndSwitchSet == null)
            {
                loopAndSwitchSet = new ObjArray();
            }
            loopAndSwitchSet.push(switchNode);
            return switchNode;
        }

        private void exitSwitch()
        {
            loopAndSwitchSet.pop();
        }
        /// <summary>
        /// 赋值表达式
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node assignExpr(bool inForInit)
        {
            int tt = peekToken();
            if (tt == Token.YIELD)
            {
                consumeToken();
                return returnOrYield(tt, true);
            }
            Node pn = condExpr(inForInit);

            tt = peekToken();
            if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN)
            {
                consumeToken();
                decompiler.addToken(tt);
                pn = nf.createAssignment(tt, pn, assignExpr(inForInit));
            }

            return pn;
        }

        public void pushScope(Node node)
        {
            Node.Scope scopeNode = (Node.Scope)node;
            if (scopeNode.getParentScope() != null) throw Kit.codeBug();
            scopeNode.setParent(currentScope);
            currentScope = scopeNode;
        }

        public void popScope()
        {
            currentScope = currentScope.getParentScope();
        }

        public int getCurrentLineNumber()
        {
            return ts.getLineno();
        }
        
        /// <summary>
        /// 或表达示
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node orExpr(bool inForInit)
        {
            Node pn = andExpr(inForInit);
            if (matchToken(Token.OR))
            {
                decompiler.addToken(Token.OR);
                pn = nf.createBinary(Token.OR, pn, orExpr(inForInit));
            }

            return pn;
        }
        /// <summary>
        /// And表达示
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node andExpr(bool inForInit)
        {
            Node pn = bitOrExpr(inForInit);
            if (matchToken(Token.AND))
            {
                decompiler.addToken(Token.AND);
                pn = nf.createBinary(Token.AND, pn, andExpr(inForInit));
            }

            return pn;
        }
        /// <summary>
        /// 按位或
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node bitOrExpr(bool inForInit)
        {
            Node pn = bitXorExpr(inForInit);
            while (matchToken(Token.BITOR))
            {
                decompiler.addToken(Token.BITOR);
                pn = nf.createBinary(Token.BITOR, pn, bitXorExpr(inForInit));
            }
            return pn;
        }
        /// <summary>
        /// 按位且
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node bitAndExpr(bool inForInit)
        {
            Node pn = eqExpr(inForInit);
            while (matchToken(Token.BITAND))
            {
                decompiler.addToken(Token.BITAND);
                pn = nf.createBinary(Token.BITAND, pn, eqExpr(inForInit));
            }
            return pn;
        }
        /// <summary>
        /// 等号表达示
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node eqExpr(bool inForInit)
        {
            Node pn = relExpr(inForInit);
            for (; ; )
            {
                int tt = peekToken();
                switch (tt)
                {
                    case Token.EQ:
                    case Token.NE:
                    case Token.SHEQ:
                    case Token.SHNE:
                        consumeToken();
                        int decompilerToken = tt;
                        int parseToken = tt;
                        if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2)
                        {
                            // JavaScript 1.2 uses shallow equality for == and != .
                            // In addition, convert === and !== for decompiler into
                            // == and != since the decompiler is supposed to show
                            // canonical source and in 1.2 ===, !== are allowed
                            // only as an alias to ==, !=.
                            switch (tt)
                            {
                                case Token.EQ:
                                    parseToken = Token.SHEQ;
                                    break;
                                case Token.NE:
                                    parseToken = Token.SHNE;
                                    break;
                                case Token.SHEQ:
                                    decompilerToken = Token.EQ;
                                    break;
                                case Token.SHNE:
                                    decompilerToken = Token.NE;
                                    break;
                            }
                        }
                        decompiler.addToken(decompilerToken);
                        pn = nf.createBinary(parseToken, pn, relExpr(inForInit));
                        continue;
                }
                break;
            }
            return pn;
        }
        /// <summary>
        /// 关系表达示
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node relExpr(bool inForInit)
        {
            Node pn = shiftExpr();
            for (; ; )
            {
                int tt = peekToken();
                switch (tt)
                {
                    case Token.IN:
                        if (inForInit)
                            break;
                        consumeToken();//这里我改写了一下
                        decompiler.addToken(tt);
                        pn = nf.createBinary(tt, pn, shiftExpr());
                        continue;
                    // fall through
                    case Token.INSTANCEOF:
                    case Token.LE:
                    case Token.LT:
                    case Token.GE:
                    case Token.GT:
                        consumeToken();
                        decompiler.addToken(tt);
                        pn = nf.createBinary(tt, pn, shiftExpr());
                        continue;
                }
                break;
            }
            return pn;
        }
        /// <summary>
        /// 转换表达示 
        /// </summary>
        /// <returns></returns>
        private Node shiftExpr()
        {
            Node pn = addExpr();
            for (; ; )
            {
                int tt = peekToken();
                switch (tt)
                {
                    case Token.LSH:
                    case Token.URSH:
                    case Token.RSH:
                        consumeToken();
                        decompiler.addToken(tt);
                        pn = nf.createBinary(tt, pn, addExpr());
                        continue;
                }
                break;
            }
            return pn;
        }
        /// <summary>
        /// 加表达式
        /// </summary>
        /// <returns></returns>
        private Node addExpr()
        {
            Node pn = mulExpr();
            for (; ; )
            {
                int tt = peekToken();
                if (tt == Token.ADD || tt == Token.SUB)
                {
                    consumeToken();
                    decompiler.addToken(tt);
                    // flushNewLines
                    pn = nf.createBinary(tt, pn, mulExpr());
                    continue;
                }
                break;
            }

            return pn;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private Node mulExpr()
        {
            Node pn = unaryExpr();
            for (; ; )
            {
                int tt = peekToken();
                switch (tt)
                {
                    case Token.MUL:
                    case Token.DIV:
                    case Token.MOD:
                        consumeToken();
                        decompiler.addToken(tt);
                        pn = nf.createBinary(tt, pn, unaryExpr());
                        continue;
                }
                break;
            }

            return pn;
        }
        /// <summary>
        /// 一元表达示
        /// </summary>
        /// <returns></returns>
        private Node unaryExpr()
        {
            int tt;

            tt = peekToken();

            switch (tt)
            {
                case Token.VOID:
                case Token.NOT:
                case Token.BITNOT:
                case Token.TYPEOF:
                    consumeToken();
                    decompiler.addToken(tt);
                    return nf.createUnary(tt, unaryExpr());

                case Token.ADD:
                    consumeToken();
                    // Convert to special POS token in decompiler and parse tree
                    decompiler.addToken(Token.POS);
                    return nf.createUnary(Token.POS, unaryExpr());

                case Token.SUB:
                    consumeToken();
                    // Convert to special NEG token in decompiler and parse tree
                    decompiler.addToken(Token.NEG);
                    return nf.createUnary(Token.NEG, unaryExpr());

                case Token.INC:
                case Token.DEC:
                    consumeToken();
                    decompiler.addToken(tt);
                    return nf.createIncDec(tt, false, memberExpr(true));

                case Token.DELPROP:
                    consumeToken();
                    decompiler.addToken(Token.DELPROP);
                    return nf.createUnary(Token.DELPROP, unaryExpr());

                case Token.ERROR:
                    consumeToken();
                    break;

                // XML stream encountered in expression.
                case Token.LT:
                    if (compilerEnv.isXmlAvailable())
                    {
                        consumeToken();
                        Node pn = xmlInitializer();
                        return memberExprTail(true, pn);
                    }
                    //这里我改写的
                    Node pn3 = memberExpr(true);

                    // Don't look across a newline boundary for a postfix incop.
                    tt = peekTokenOrEOL();
                    if (tt == Token.INC || tt == Token.DEC)
                    {
                        consumeToken();
                        decompiler.addToken(tt);
                        return nf.createIncDec(tt, true, pn3);
                    }
                    return pn3;
                // Fall thru to the default handling of RELOP

                default:
                    Node pn2 = memberExpr(true);

                    
                    tt = peekTokenOrEOL();
                    if (tt == Token.INC || tt == Token.DEC)
                    {
                        consumeToken();
                        decompiler.addToken(tt);
                        return nf.createIncDec(tt, true, pn2);
                    }
                    return pn2;
            }
            return nf.createName("error"); // Only reached on error.Try to continue.

        }



        private Node xmlInitializer()
        {
            int tt = ts.getFirstXMLToken();
            if (tt != Token.XML && tt != Token.XMLEND)
            {
                reportError("msg.syntax");
                return null;
            }

            /* Make a NEW node to append to. */
            Node pnXML = nf.createLeaf(Token.NEW);

            String xml = ts.getString();
            bool fAnonymous = xml.Trim().StartsWith("<>");

            Node pn = nf.createName(fAnonymous ? "XMLList" : "XML");
            nf.addChildToBack(pnXML, pn);

            pn = null;
            Node expr1;
            for (; ; tt = ts.getNextXMLToken())
            {
                switch (tt)
                {
                    case Token.XML:
                        xml = ts.getString();
                        decompiler.addName(xml);
                        mustMatchToken(Token.LC, "msg.syntax");
                        decompiler.addToken(Token.LC);
                        expr1 = (peekToken() == Token.RC)
                            ? nf.createString("")
                            : (Expr(false));
                        mustMatchToken(Token.RC, "msg.syntax");
                        decompiler.addToken(Token.RC);
                        if (pn == null)
                        {
                            pn = nf.createString(xml);
                        }
                        else
                        {
                            pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
                        }
                        if (ts.isXMLAttribute())
                        {
                            /* Need to put the result in double quotes */
                            expr1 = nf.createUnary(Token.ESCXMLATTR, expr1);
                            Node prepend = nf.createBinary(Token.ADD,
                                                           nf.createString("\""),
                                                           expr1);
                            expr1 = nf.createBinary(Token.ADD,
                                                   prepend,
                                                   nf.createString("\""));
                        }
                        else
                        {
                            expr1 = nf.createUnary(Token.ESCXMLTEXT, expr1);
                        }
                        pn = nf.createBinary(Token.ADD, pn, expr1);
                        break;
                    case Token.XMLEND:
                        xml = ts.getString();
                        decompiler.addName(xml);
                        if (pn == null)
                        {
                            pn = nf.createString(xml);
                        }
                        else
                        {
                            pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
                        }

                        nf.addChildToBack(pnXML, pn);
                        return pnXML;
                    default:
                        reportError("msg.syntax");
                        return null;
                }
            }
        }

        /// <summary>
        /// 成员表达示
        /// </summary>
        /// <param name="allowCallSyntax"></param>
        /// <returns></returns>
        private Node memberExpr(bool allowCallSyntax)
        {
            int tt;

            Node pn;

            //是否是New表达示
            tt = peekToken();
            if (tt == Token.NEW)
            {
                //清空
                consumeToken();
                decompiler.addToken(Token.NEW);

                //构建一个new节点
                pn = nf.createCallOrNew(Token.NEW, memberExpr(false));

                if (matchToken(Token.LP))
                {
                    decompiler.addToken(Token.LP);
                    //加入这些参数
                    argumentList(pn);
                }

                tt = peekToken();
                if (tt == Token.LC)
                {
                    nf.addChildToBack(pn, primaryExpr());
                }
            }
            else
            {
                pn = primaryExpr();
            }

            return memberExprTail(allowCallSyntax, pn);
        }
        private void mustHaveXML()
        {
            if (!compilerEnv.isXmlAvailable())
            {
                reportError("msg.XML.not.available");
            }
        }

        private Node memberExprTail(bool allowCallSyntax, Node pn)
        {

            for (; ; )
            {
                int tt = peekToken();
                switch (tt)
                {

                    case Token.DOT:
                    case Token.DOTDOT:
                        {
                            int memberTypeFlags;
                            String s;

                            consumeToken();
                            decompiler.addToken(tt);
                            memberTypeFlags = 0;
                            if (tt == Token.DOTDOT)
                            {
                                mustHaveXML();
                                memberTypeFlags = Node.DESCENDANTS_FLAG;
                            }
                            if (!compilerEnv.isXmlAvailable())
                            {
                                mustMatchToken(Token.NAME, "msg.no.name.after.dot");
                                s = ts.getString();
                                decompiler.addName(s);
                                pn = nf.createPropertyGet(pn, null, s, memberTypeFlags);
                                break;
                            }

                            tt = nextToken();
                            switch (tt)
                            {

                                // needed for generator.throw();
                                case Token.THROW:
                                    decompiler.addName("throw");
                                    pn = propertyName(pn, "throw", memberTypeFlags);
                                    break;

                                // handles: name, ns::name, ns::*, ns::[expr]
                                case Token.NAME:
                                    s = ts.getString();
                                    decompiler.addName(s);
                                    pn = propertyName(pn, s, memberTypeFlags);
                                    break;

                                // handles: *, *::name, *::*, *::[expr]
                                case Token.MUL:
                                    decompiler.addName("*");
                                    pn = propertyName(pn, "*", memberTypeFlags);
                                    break;

                                // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
                                //          '@::attr', '@::*', '@*', '@*::attr', '@*::*'
                                case Token.XMLATTR:
                                    decompiler.addToken(Token.XMLATTR);
                                    pn = attributeAccess(pn, memberTypeFlags);
                                    break;

                                default:
                                    reportError("msg.no.name.after.dot");
                                    break;
                            }
                        }
                        break;

                    case Token.DOTQUERY:
                        consumeToken();
                        mustHaveXML();
                        decompiler.addToken(Token.DOTQUERY);
                        pn = nf.createDotQuery(pn, Expr(false), ts.getLineno());
                        mustMatchToken(Token.RP, "msg.no.paren");
                        decompiler.addToken(Token.RP);
                        break;

                    case Token.LB:
                        consumeToken();
                        decompiler.addToken(Token.LB);
                        pn = nf.createElementGet(pn, null, Expr(false), 0);
                        mustMatchToken(Token.RB, "msg.no.bracket.index");
                        decompiler.addToken(Token.RB);
                        break;

                    case Token.LP:
                        if (!allowCallSyntax)
                        {
                            goto tailLoop;
                        }
                        consumeToken();
                        decompiler.addToken(Token.LP);
                        pn = nf.createCallOrNew(Token.CALL, pn);
                        //加入参数
                        argumentList(pn);
                        break;

                    default:
                        goto tailLoop;
                }
            }
        tailLoop:
            return pn;
        }
        /// <summary>
        /// 参数列表
        /// </summary>
        /// <param name="listNode"></param>
        private void argumentList(Node listNode)
        {
            bool matched;
            matched = matchToken(Token.RP);
            if (!matched)
            {
                bool first = true;
                do
                {
                    if (!first)
                        decompiler.addToken(Token.COMMA);
                    first = false;
                    if (peekToken() == Token.YIELD)
                    {//括号表达示
                        reportError("msg.yield.parenthesized");
                    }
                    nf.addChildToBack(listNode, assignExpr(false));
                } while (matchToken(Token.COMMA));

                mustMatchToken(Token.RP, "msg.no.paren.arg");
            }
            decompiler.addToken(Token.RP);
        }

        /*
     * Xml attribute expression:
     *   '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*'
     */
        private Node attributeAccess(Node pn, int memberTypeFlags)
        {
            memberTypeFlags |= Node.ATTRIBUTE_FLAG;
            int tt = nextToken();

            switch (tt)
            {
                // handles: @name, @ns::name, @ns::*, @ns::[expr]
                case Token.NAME:
                    {
                        String s = ts.getString();
                        decompiler.addName(s);
                        pn = propertyName(pn, s, memberTypeFlags);
                    }
                    break;

                // handles: @*, @*::name, @*::*, @*::[expr]
                case Token.MUL:
                    decompiler.addName("*");
                    pn = propertyName(pn, "*", memberTypeFlags);
                    break;

                // handles @[expr]
                case Token.LB:
                    decompiler.addToken(Token.LB);
                    pn = nf.createElementGet(pn, null, Expr(false), memberTypeFlags);
                    mustMatchToken(Token.RB, "msg.no.bracket.index");
                    decompiler.addToken(Token.RB);
                    break;

                default:
                    reportError("msg.no.name.after.xmlAttr");
                    pn = nf.createPropertyGet(pn, null, "?", memberTypeFlags);
                    break;
            }

            return pn;
        }

        /// <summary>
        /// ::引用
        /// </summary>
        /// <param name="pn"></param>
        /// <param name="name"></param>
        /// <param name="memberTypeFlags"></param>
        /// <returns></returns>
        private Node propertyName(Node pn, String name, int memberTypeFlags)
        {
            String namespace1 = null;
            if (matchToken(Token.COLONCOLON))
            {
                decompiler.addToken(Token.COLONCOLON);
                namespace1 = name;

                int tt = nextToken();
                switch (tt)
                {
                    //  name::name
                    case Token.NAME:
                        name = ts.getString();
                        decompiler.addName(name);
                        break;

                    //  name::*
                    case Token.MUL:
                        decompiler.addName("*");
                        name = "*";
                        break;

                    //  name::[expr]
                    case Token.LB:
                        decompiler.addToken(Token.LB);
                        pn = nf.createElementGet(pn, namespace1, Expr(false),
                                                 memberTypeFlags);
                        mustMatchToken(Token.RB, "msg.no.bracket.index");
                        decompiler.addToken(Token.RB);
                        return pn;

                    default:
                        reportError("msg.no.name.after.coloncolon");
                        name = "?";
                        break;
                }
            }

            pn = nf.createPropertyGet(pn, namespace1, name, memberTypeFlags);
            return pn;
        }




        private int nextToken()
        {
            int tt = peekToken();
            consumeToken();
            return tt;
        }
        /// <summary>
        /// 按位异或
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node bitXorExpr(bool inForInit)
        {
            Node pn = bitAndExpr(inForInit);
            while (matchToken(Token.BITXOR))
            {
                decompiler.addToken(Token.BITXOR);
                pn = nf.createBinary(Token.BITXOR, pn, bitAndExpr(inForInit));
            }
            return pn;
        }
        /// <summary>
        /// 三元运算符
        /// </summary>
        /// <param name="inForInit"></param>
        /// <returns></returns>
        private Node condExpr(bool inForInit)
        {
            Node pn = orExpr(inForInit);

            if (matchToken(Token.HOOK))
            {
                decompiler.addToken(Token.HOOK);
                Node ifTrue = assignExpr(false);
                mustMatchToken(Token.COLON, "msg.no.colon.cond");
                decompiler.addToken(Token.COLON);
                Node ifFalse = assignExpr(inForInit);
                return nf.createCondExpr(pn, ifTrue, ifFalse);
            }

            return pn;
        }

        /// <summary>
        /// 内部方法
        /// </summary>
        /// <returns></returns>
        public bool insideFunction()
        {
            return nestingOfFunction != 0;
        }
        private Node returnOrYield(int tt, bool exprContext)
        {
            if (!insideFunction())
            {
                reportError(tt == Token.RETURN ? "msg.bad.return"
                                               : "msg.bad.yield");
            }
            consumeToken();
            decompiler.addToken(tt);
            int lineno = ts.getLineno();

            Node e;
            /* This is ugly, but we don't want to require a semicolon. */
            switch (peekTokenOrEOL())
            {
                case Token.SEMI:
                case Token.RC:
                case Token.EOF:
                case Token.EOL:
                case Token.ERROR:
                case Token.RB:
                case Token.RP:
                case Token.YIELD:
                    e = null;
                    break;
                default:
                    e = Expr(false);
                    break;
            }

            int before = endFlags;
            Node ret;

            if (tt == Token.RETURN)
            {
                if (e == null)
                {
                    endFlags |= Node.END_RETURNS;
                }
                else
                {
                    endFlags |= Node.END_RETURNS_VALUE;
                }
                ret = nf.createReturn(e, lineno);

                // see if we need a strict mode warning
                if (nowAllSet(before, endFlags,
                              Node.END_RETURNS | Node.END_RETURNS_VALUE))
                {
                    addStrictWarning("msg.return.inconsistent", "");
                }
            }
            else
            {
                endFlags |= Node.END_YIELDS;
                ret = nf.createYield(e, lineno);
                if (!exprContext)
                    ret = new Node(Token.EXPR_VOID, ret, lineno);
            }

            // see if we are mixing yields and value returns.
            if (nowAllSet(before, endFlags,
                          Node.END_YIELDS | Node.END_RETURNS_VALUE))
            {
                String name = ((FunctionNode)currentScriptOrFn).getFunctionName();
                if (name.Length == 0)
                    addError("msg.anon.generator.returns", "");
                else
                    addError("msg.generator.returns", name);
            }

            return ret;
        }


        /// <summary>
        /// Returns whether or not the bits in the mask have changed to all set.
        /// </summary>
        /// <param name="before"></param>
        /// <param name="after"></param>
        /// <param name="mask"></param>
        /// <returns></returns>
        private static bool nowAllSet(int before, int after, int mask)
        {
            return ((before & mask) != mask) && ((after & mask) == mask);
        }

        private Node Expr(bool inForInit)
        {
            Node pn = assignExpr(inForInit);
            while (matchToken(Token.COMMA))
            {
                decompiler.addToken(Token.COMMA);
                if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
                    addStrictWarning("msg.no.side.effects", "");
                if (peekToken() == Token.YIELD)
                {
                    reportError("msg.yield.parenthesized");
                }
                pn = nf.createBinary(Token.COMMA, pn, assignExpr(inForInit));
            }
            return pn;
        }



        private int peekTokenOrEOL()
        {
            int tt = peekToken();
            // Check for last peeked token flags
            if ((currentFlaggedToken & TI_AFTER_EOL) != 0)
            {
                tt = Token.EOL;
            }
            return tt;
        }

        public void defineSymbol(int declType, bool ignoreNotInBlock, String name)
        {
            Node.Scope definingScope = currentScope.getDefiningScope(name);
            Node.Scope.Symbol symbol = definingScope != null
                                  ? definingScope.getSymbol(name)
                                  : null;
            bool error = false;
            if (symbol != null && (symbol.declType == Token.CONST ||
            declType == Token.CONST))
            {
                error = true;
            }
            else
            {
                switch (declType)
                {
                    case Token.LET:
                        if (symbol != null && definingScope == currentScope)
                        {
                            error = symbol.declType == Token.LET;
                        }
                        int currentScopeType = currentScope.getType();
                        if (!ignoreNotInBlock &&
                            ((currentScopeType == Token.LOOP) ||
                             (currentScopeType == Token.IF)))
                        {
                            addError("msg.let.decl.not.in.block");
                        }
                        currentScope.putSymbol(name,
                            new Node.Scope.Symbol(declType, name));
                        break;

                    case Token.VAR:
                    case Token.CONST:
                    case Token.FUNCTION:
                        if (symbol != null)
                        {
                            if (symbol.declType == Token.VAR)
                                addStrictWarning("msg.var.redecl", name);
                            else if (symbol.declType == Token.LP)
                            {
                                addStrictWarning("msg.var.hides.arg", name);
                            }
                        }
                        else
                        {
                            currentScriptOrFn.putSymbol(name,
                                new Node.Scope.Symbol(declType, name));
                        }
                        break;

                    case Token.LP:
                        if (symbol != null)
                        {
                            // must be duplicate parameter. Second parameter hides the 
                            // first, so go ahead and add the second pararameter
                            addWarning("msg.dup.parms", name);
                        }
                        currentScriptOrFn.putSymbol(name,
                            new Node.Scope.Symbol(declType, name));
                        break;

                    default:
                        throw Kit.codeBug();
                }
            }
            if (error)
            {
                addError(symbol.declType == Token.CONST ? "msg.const.redecl" :
                         symbol.declType == Token.LET ? "msg.let.redecl" :
                         symbol.declType == Token.VAR ? "msg.var.redecl" :
                         symbol.declType == Token.FUNCTION ? "msg.fn.redecl" :
                         "msg.parm.redecl", name);
            }
        }
        public void addError(String messageId, String messageArg)
        {
            ++syntaxErrorCount;
            String message = ScriptRuntime.getMessage1(messageId, messageArg);
            errorReporter.error(message, sourceURI, ts.getLineno(),
                                ts.getLine(), ts.getOffset());
        }
        public void addStrictWarning(String messageId, String messageArg)
        {
            if (compilerEnv.isStrictMode())
                addWarning(messageId, messageArg);
        }
        public void addWarning(String messageId, String messageArg)
        {
            String message = ScriptRuntime.getMessage1(messageId, messageArg);
            if (compilerEnv.reportWarningAsError())
            {
                ++syntaxErrorCount;
                errorReporter.error(message, sourceURI, ts.getLineno(),
                                    ts.getLine(), ts.getOffset());
            }
            else
                errorReporter.warning(message, sourceURI, ts.getLineno(),
                                      ts.getLine(), ts.getOffset());
        }
        /// <summary>
        /// 报告错误
        /// </summary>
        /// <param name="messageId"></param>
        /// <returns></returns>
        public Exception reportError(String messageId)
        {
            addError(messageId);

            // Throw a ParserException exception to unwind the recursive descent
            // parse.
            throw new ParserException();
        }

        /// <summary>
        /// 必须向前的标记
        /// </summary>
        /// <param name="toMatch"></param>
        /// <param name="messageId"></param>
        private void mustMatchToken(int toMatch, String messageId)
        {
            if (!matchToken(toMatch))
                reportError(messageId);
        }
        /// <summary>
        /// 是否可以向前
        /// </summary>
        /// <param name="toMatch"></param>
        /// <returns></returns>
        private bool matchToken(int toMatch)
        {
            int tt = peekToken();
            if (tt != toMatch)
                return false;
            consumeToken();
            return true;
        }

        private Node function(int functionType)
        {
            int syntheticType = functionType;
            int baseLineno = ts.getLineno();  // line number where source starts

            int functionSourceStart = decompiler.markFunctionStart(functionType);
            String name;
            Node memberExprNode = null;
            if (matchToken(Token.NAME))
            {
                name = ts.getString();
                decompiler.addName(name);
                if (!matchToken(Token.LP))
                {
                    if (compilerEnv.isAllowMemberExprAsFunctionName())
                    {
                        // Extension to ECMA: if 'function <name>' does not follow
                        // by '(', assume <name> starts memberExpr
                        Node memberExprHead = nf.createName(name);
                        name = "";
                        memberExprNode = memberExprTail(false, memberExprHead);
                    }
                    mustMatchToken(Token.LP, "msg.no.paren.parms");
                }
            }
            else if (matchToken(Token.LP))
            {
                // Anonymous function
                name = "";
            }
            else
            {
                name = "";
                if (compilerEnv.isAllowMemberExprAsFunctionName())
                {
                    // Note that memberExpr can not start with '(' like
                    // in function (1+2).toString(), because 'function (' already
                    // processed as anonymous function
                    memberExprNode = memberExpr(false);
                }
                mustMatchToken(Token.LP, "msg.no.paren.parms");
            }

            if (memberExprNode != null)
            {
                syntheticType = FunctionNode.FUNCTION_EXPRESSION;
            }

            if (syntheticType != FunctionNode.FUNCTION_EXPRESSION &&
                name.Length > 0)
            {
                // Function statements define a symbol in the enclosing scope
                defineSymbol(Token.FUNCTION, false, name);
            }

            bool nested = insideFunction();

            FunctionNode fnNode = nf.createFunction(name);
            if (nested || nestingOfWith > 0)
            {
                // 1. Nested functions are not affected by the dynamic scope flag
                // as dynamic scope is already a parent of their scope.
                // 2. Functions defined under the with statement also immune to
                // this setup, in which case dynamic scope is ignored in favor
                // of with object.
                fnNode.itsIgnoreDynamicScope = true;
            }
            int functionIndex = currentScriptOrFn.addFunction(fnNode);

            int functionSourceEnd;

            ScriptOrFnNode savedScriptOrFn = currentScriptOrFn;
            currentScriptOrFn = fnNode;
            Node.Scope savedCurrentScope = currentScope;
            currentScope = fnNode;
            int savedNestingOfWith = nestingOfWith;
            nestingOfWith = 0;
            IDictionary<String, Node> savedLabelSet = labelSet;
            labelSet = null;
            ObjArray savedLoopSet = loopSet;
            loopSet = null;
            ObjArray savedLoopAndSwitchSet = loopAndSwitchSet;
            loopAndSwitchSet = null;
            int savedFunctionEndFlags = endFlags;
            endFlags = 0;

            Node destructuring = null;
            Node body;
            try
            {
                decompiler.addToken(Token.LP);
                if (!matchToken(Token.RP))
                {
                    bool first = true;
                    do
                    {
                        if (!first)
                            decompiler.addToken(Token.COMMA);
                        first = false;
                        int tt = peekToken();
                        if (tt == Token.LB || tt == Token.LC)
                        {
                            // Destructuring assignment for parameters: add a 
                            // dummy parameter name, and add a statement to the
                            // body to initialize variables from the destructuring
                            // assignment
                            if (destructuring == null)
                            {
                                destructuring = new Node(Token.COMMA);
                            }
                            String parmName = currentScriptOrFn.getNextTempName();
                            defineSymbol(Token.LP, false, parmName);
                            destructuring.addChildToBack(
                                nf.createDestructuringAssignment(Token.VAR,
                                    primaryExpr(), nf.createName(parmName)));
                        }
                        else
                        {
                            mustMatchToken(Token.NAME, "msg.no.parm");
                            String s = ts.getString();
                            defineSymbol(Token.LP, false, s);
                            decompiler.addName(s);
                        }
                    } while (matchToken(Token.COMMA));

                    mustMatchToken(Token.RP, "msg.no.paren.after.parms");
                }
                decompiler.addToken(Token.RP);

                mustMatchToken(Token.LC, "msg.no.brace.body");
                decompiler.addEOL(Token.LC);
                body = parseFunctionBody();
                if (destructuring != null)
                {
                    body.addChildToFront(
                        new Node(Token.EXPR_VOID, destructuring, ts.getLineno()));
                }
                mustMatchToken(Token.RC, "msg.no.brace.after.body");

                if (compilerEnv.isStrictMode() && !body.hasConsistentReturnUsage())
                {
                    String msg = name.Length > 0 ? "msg.no.return.value"
                                                   : "msg.anon.no.return.value";
                    addStrictWarning(msg, name);
                }

                if (syntheticType == FunctionNode.FUNCTION_EXPRESSION &&
                    name.Length > 0 && currentScope.getSymbol(name) == null)
                {
                    // Function expressions define a name only in the body of the 
                    // function, and only if not hidden by a parameter name
                    defineSymbol(Token.FUNCTION, false, name);
                }

                decompiler.addToken(Token.RC);
                functionSourceEnd = decompiler.markFunctionEnd(functionSourceStart);
                if (functionType != FunctionNode.FUNCTION_EXPRESSION)
                {
                    // Add EOL only if function is not part of expression
                    // since it gets SEMI + EOL from Statement in that case
                    decompiler.addToken(Token.EOL);
                }
            }
            finally
            {
                endFlags = savedFunctionEndFlags;
                loopAndSwitchSet = savedLoopAndSwitchSet;
                loopSet = savedLoopSet;
                labelSet = savedLabelSet;
                nestingOfWith = savedNestingOfWith;
                currentScriptOrFn = savedScriptOrFn;
                currentScope = savedCurrentScope;
            }

            fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd);
            fnNode.setSourceName(sourceURI);
            fnNode.setBaseLineno(baseLineno);
            fnNode.setEndLineno(ts.getLineno());

            Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType);
            if (memberExprNode != null)
            {
                pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
                if (functionType != FunctionNode.FUNCTION_EXPRESSION)
                {
                    // XXX check JScript behavior: should it be createExprStatement?
                    pn = nf.createExprStatementNoReturn(pn, baseLineno);
                }
            }
            return pn;
        }

        /*
     * The C version of this function takes an argument list,
     * which doesn't seem to be needed for tree generation...
     * it'd only be useful for checking argument hiding, which
     * I'm not doing anyway...
     */
        private Node parseFunctionBody()
        {
            ++nestingOfFunction;
            Node pn = nf.createBlock(ts.getLineno());
            try
            {
                for (; ; )
                {
                    Node n;
                    int tt = peekToken();
                    switch (tt)
                    {
                        case Token.ERROR:
                        case Token.EOF:
                        case Token.RC:
                            goto bodyLoop;

                        case Token.FUNCTION:
                            consumeToken();
                            n = function(FunctionNode.FUNCTION_STATEMENT);
                            break;
                        default:
                            n = statement();
                            break;
                    }
                    nf.addChildToBack(pn, n);
                }
            bodyLoop: ;
            }
            catch (ParserException e)
            {
                // Ignore it
            }
            finally
            {
                --nestingOfFunction;
            }

            return pn;
        }

        private Node arrayComprehension(String arrayName, Node expr1)
        {
            if (nextToken() != Token.FOR)
                throw Kit.codeBug(); // shouldn't be here if next token isn't 'for'
            decompiler.addName(" "); // space after array literal expr
            decompiler.addToken(Token.FOR);
            bool isForEach = false;
            if (matchToken(Token.NAME))
            {
                decompiler.addName(ts.getString());
                if (ts.getString() == "each")
                {
                    isForEach = true;
                }
                else
                {
                    reportError("msg.no.paren.for");
                }
            }
            mustMatchToken(Token.LP, "msg.no.paren.for");
            decompiler.addToken(Token.LP);
            String name;
            int tt = peekToken();
            if (tt == Token.LB || tt == Token.LC)
            {
                // handle destructuring assignment
                name = currentScriptOrFn.getNextTempName();
                defineSymbol(Token.LP, false, name);
                expr1 = nf.createBinary(Token.COMMA,
                    nf.createAssignment(Token.ASSIGN, primaryExpr(),
                                        nf.createName(name)),
                    expr1);
            }
            else if (tt == Token.NAME)
            {
                consumeToken();
                name = ts.getString();
                decompiler.addName(name);
            }
            else
            {
                reportError("msg.bad.var");
                return nf.createNumber(0);
            }

            Node init = nf.createName(name);
            // Define as a let since we want the scope of the variable to
            // be restricted to the array comprehension
            defineSymbol(Token.LET, false, name);

            mustMatchToken(Token.IN, "msg.in.after.for.name");
            decompiler.addToken(Token.IN);
            Node iterator = Expr(false);
            mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
            decompiler.addToken(Token.RP);

            Node body;
            tt = peekToken();
            if (tt == Token.FOR)
            {
                body = arrayComprehension(arrayName, expr1);
            }
            else
            {
                Node call = nf.createCallOrNew(Token.CALL,
                    nf.createPropertyGet(nf.createName(arrayName), null,
                                         "push", 0));
                call.addChildToBack(expr1);
                body = new Node(Token.EXPR_VOID, call, ts.getLineno());
                if (tt == Token.IF)
                {
                    consumeToken();
                    decompiler.addToken(Token.IF);
                    int lineno = ts.getLineno();
                    Node cond = condition();
                    body = nf.createIf(cond, body, null, lineno);
                }
                mustMatchToken(Token.RB, "msg.no.bracket.arg");
                decompiler.addToken(Token.RB);
            }

            Node loop = enterLoop(null, true);
            try
            {
                return nf.createForIn(Token.LET, loop, init, iterator, body,
                                      isForEach);
            }
            finally
            {
                exitLoop(false);
            }
        }

        private Node enterLoop(Node loopLabel, bool doPushScope)
        {
            Node loop = nf.createLoopNode(loopLabel, ts.getLineno());
            if (loopSet == null)
            {
                loopSet = new ObjArray();
                if (loopAndSwitchSet == null)
                {
                    loopAndSwitchSet = new ObjArray();
                }
            }
            loopSet.push(loop);
            loopAndSwitchSet.push(loop);
            if (doPushScope)
            {
                pushScope(loop);
            }
            return loop;
        }

        private void exitLoop(bool doPopScope)
        {
            loopSet.pop();
            loopAndSwitchSet.pop();
            if (doPopScope)
            {
                popScope();
            }
        }

        private Node condition()
        {
            mustMatchToken(Token.LP, "msg.no.paren.cond");
            decompiler.addToken(Token.LP);
            Node pn = Expr(false);
            mustMatchToken(Token.RP, "msg.no.paren.after.cond");
            decompiler.addToken(Token.RP);

            // Report strict warning on code like "if (a = 7) ...". Suppress the
            // warning if the condition is parenthesized, like "if ((a = 7)) ...".
            if (pn.getProp(Node.PARENTHESIZED_PROP) == null &&
                (pn.getType() == Token.SETNAME || pn.getType() == Token.SETPROP ||
                 pn.getType() == Token.SETELEM))
            {
                addStrictWarning("msg.equal.as.assign", "");
            }
            return pn;
        }
        /// <summary>
        /// 主表达示
        /// </summary>
        /// <returns></returns>
        private Node primaryExpr()
        {
            Node pn;

            int ttFlagged = nextFlaggedToken();
            int tt = ttFlagged & CLEAR_TI_MASK;

            switch (tt)
            {

                case Token.FUNCTION:
                    return function(FunctionNode.FUNCTION_EXPRESSION);

                case Token.LB:
                    {
                        ObjArray elems = new ObjArray();
                        int skipCount = 0;
                        int destructuringLen = 0;
                        decompiler.addToken(Token.LB);
                        bool after_lb_or_comma = true;
                        for (; ; )
                        {
                            tt = peekToken();

                            if (tt == Token.COMMA)
                            {
                                consumeToken();
                                decompiler.addToken(Token.COMMA);
                                if (!after_lb_or_comma)
                                {
                                    after_lb_or_comma = true;
                                }
                                else
                                {
                                    elems.add(null);
                                    ++skipCount;
                                }
                            }
                            else if (tt == Token.RB)
                            {
                                consumeToken();
                                decompiler.addToken(Token.RB);
                                // for ([a,] in obj) is legal, but for ([a] in obj) is 
                                // not since we have both key and value supplied. The
                                // trick is that [a,] and [a] are equivalent in other
                                // array literal contexts. So we calculate a special
                                // length value just for destructuring assignment.
                                destructuringLen = elems.size() +
                                                   (after_lb_or_comma ? 1 : 0);
                                break;
                            }
                            else if (skipCount == 0 && elems.size() == 1 &&
                                     tt == Token.FOR)
                            {
                                Node scopeNode = nf.createScopeNode(Token.ARRAYCOMP,
                                                                    ts.getLineno());
                                String tempName = currentScriptOrFn.getNextTempName();
                                pushScope(scopeNode);
                                try
                                {
                                    defineSymbol(Token.LET, false, tempName);
                                    Node expr = (Node)elems.get(0);
                                    Node block = nf.createBlock(ts.getLineno());
                                    Node init = new Node(Token.EXPR_VOID,
                                        nf.createAssignment(Token.ASSIGN,
                                            nf.createName(tempName),
                                            nf.createCallOrNew(Token.NEW,
                                                nf.createName("Array"))), ts.getLineno());
                                    block.addChildToBack(init);
                                    block.addChildToBack(arrayComprehension(tempName,
                                        expr));
                                    scopeNode.addChildToBack(block);
                                    scopeNode.addChildToBack(nf.createName(tempName));
                                    return scopeNode;
                                }
                                finally
                                {
                                    popScope();
                                }
                            }
                            else
                            {
                                if (!after_lb_or_comma)
                                {
                                    reportError("msg.no.bracket.arg");
                                }
                                elems.add(assignExpr(false));
                                after_lb_or_comma = false;
                            }
                        }
                        return nf.createArrayLiteral(elems, skipCount, destructuringLen);
                    }

                case Token.LC:
                    {
                        ObjArray elems = new ObjArray();
                        decompiler.addToken(Token.LC);
                        if (!matchToken(Token.RC))
                        {

                            bool first = true;

                            do
                            {
                                Object property;

                                if (!first)
                                    decompiler.addToken(Token.COMMA);
                                else
                                    first = false;

                                tt = peekToken();
                                switch (tt)
                                {
                                    case Token.NAME:
                                    case Token.STRING:
                                        consumeToken();
                                        // map NAMEs to STRINGs in object literal context
                                        // but tell the decompiler the proper type
                                        String s = ts.getString();
                                        if (tt == Token.NAME)
                                        {
                                            if (s == "get" &&
                                                peekToken() == Token.NAME)
                                            {
                                                decompiler.addToken(Token.GET);
                                                consumeToken();
                                                s = ts.getString();
                                                decompiler.addName(s);
                                                property = ScriptRuntime.getIndexObject(s);
                                                if (!getterSetterProperty(elems, property,
                                                                          true))
                                                    goto commaloop;
                                                break;
                                            }
                                            else if (s == "set" &&
                                                       peekToken() == Token.NAME)
                                            {
                                                decompiler.addToken(Token.SET);
                                                consumeToken();
                                                s = ts.getString();
                                                decompiler.addName(s);
                                                property = ScriptRuntime.getIndexObject(s);
                                                if (!getterSetterProperty(elems, property,
                                                                          false))
                                                    goto commaloop;
                                                break;
                                            }
                                            decompiler.addName(s);
                                        }
                                        else
                                        {
                                            decompiler.addString(s);
                                        }
                                        property = ScriptRuntime.getIndexObject(s);
                                        plainProperty(elems, property);
                                        break;

                                    case Token.NUMBER:
                                        consumeToken();
                                        double n = ts.getNumber();
                                        decompiler.addNumber(n);
                                        property = ScriptRuntime.getIndexObject(n);
                                        plainProperty(elems, property);
                                        break;

                                    case Token.RC:
                                        // trailing comma is OK.
                                        goto commaloop;
                                    default:
                                        reportError("msg.bad.prop");
                                        goto commaloop;
                                }
                            } while (matchToken(Token.COMMA));
                        commaloop: ;
                            mustMatchToken(Token.RC, "msg.no.brace.prop");
                        }
                        decompiler.addToken(Token.RC);
                        return nf.createObjectLiteral(elems);
                    }

                case Token.LET:
                    decompiler.addToken(Token.LET);
                    return let(false);

                case Token.LP:

                    /* Brendan's IR-jsparse.c makes a new node tagged with
                     * TOK_LP here... I'm not sure I understand why.  Isn't
                     * the grouping already implicit in the structure of the
                     * parse tree?  also TOK_LP is already overloaded (I
                     * think) in the C IR as 'function call.'  */
                    decompiler.addToken(Token.LP);
                    pn = Expr(false);
                    pn.putProp(Node.PARENTHESIZED_PROP, true);
                    decompiler.addToken(Token.RP);
                    mustMatchToken(Token.RP, "msg.no.paren");
                    return pn;

                case Token.XMLATTR:
                    mustHaveXML();
                    decompiler.addToken(Token.XMLATTR);
                    pn = attributeAccess(null, 0);
                    return pn;

                case Token.NAME:
                    {
                        String name = ts.getString();
                        if ((ttFlagged & TI_CHECK_LABEL) != 0)
                        {
                            if (peekToken() == Token.COLON)
                            {
                                // Do not consume colon, it is used as unwind indicator
                                // to return to statementHelper.
                                // XXX Better way?
                                return nf.createLabel(ts.getLineno());
                            }
                        }

                        decompiler.addName(name);
                        if (compilerEnv.isXmlAvailable())
                        {
                            pn = propertyName(null, name, 0);
                        }
                        else
                        {
                            pn = nf.createName(name);
                        }
                        return pn;
                    }

                case Token.NUMBER:
                    {
                        double n = ts.getNumber();
                        decompiler.addNumber(n);
                        return nf.createNumber(n);
                    }

                case Token.STRING:
                    {
                        String s = ts.getString();
                        decompiler.addString(s);
                        return nf.createString(s);
                    }

                case Token.DIV:
                case Token.ASSIGN_DIV:
                    {
                        // Got / or /= which should be treated as regexp in fact
                        ts.readRegExp(tt);
                        String flags = ts.regExpFlags;
                        ts.regExpFlags = null;
                        String re = ts.getString();
                        decompiler.addRegexp(re, flags);
                        int index = currentScriptOrFn.addRegexp(re, flags);
                        return nf.createRegExp(index);
                    }

                case Token.NULL:
                case Token.THIS:
                case Token.FALSE:
                case Token.TRUE:
                case Token.UNDEFINED:
                    decompiler.addToken(tt);
                    return nf.createLeaf(tt);

                case Token.RESERVED:
                    reportError("msg.reserved.id");
                    break;

                case Token.ERROR:
                    /* the scanner or one of its subroutines reported the error. */
                    break;

                case Token.EOF:
                    reportError("msg.unexpected.eof");
                    break;

                default:
                    reportError("msg.syntax");
                    break;
            }
            return null;    // should never reach here

        }


        private Node let(bool isStatement)
        {
            mustMatchToken(Token.LP, "msg.no.paren.after.let");
            decompiler.addToken(Token.LP);
            Node result = nf.createScopeNode(Token.LET, ts.getLineno());
            pushScope(result);
            try
            {
                Node vars = variables(false, Token.LET);
                nf.addChildToBack(result, vars);
                mustMatchToken(Token.RP, "msg.no.paren.let");
                decompiler.addToken(Token.RP);
                if (isStatement && peekToken() == Token.LC)
                {
                    // let statement
                    consumeToken();
                    decompiler.addEOL(Token.LC);
                    nf.addChildToBack(result, statements(null));
                    mustMatchToken(Token.RC, "msg.no.curly.let");
                    decompiler.addToken(Token.RC);
                }
                else
                {
                    // let expression
                    result.setType(Token.LETEXPR);
                    nf.addChildToBack(result, Expr(false));
                    if (isStatement)
                    {
                        // let expression in statement context
                        result = nf.createExprStatement(result, ts.getLineno());
                    }
                }
            }
            finally
            {
                popScope();
            }
            return result;
        }

        private Node statements(Node scope)
        {
            Node pn = scope != null ? scope : nf.createBlock(ts.getLineno());

            int tt;
            while ((tt = peekToken()) > Token.EOF && tt != Token.RC)
            {
                nf.addChildToBack(pn, statement());
            }

            return pn;
        }


        private int nextFlaggedToken()
        {
            peekToken();
            int ttFlagged = currentFlaggedToken;
            consumeToken();
            return ttFlagged;
        }
        private void plainProperty(ObjArray elems, Object property)
        {
            mustMatchToken(Token.COLON, "msg.no.colon.prop");

            // OBJLIT is used as ':' in object literal for
            // decompilation to solve spacing ambiguity.
            decompiler.addToken(Token.OBJECTLIT);
            elems.add(property);
            elems.add(assignExpr(false));
        }

        private bool getterSetterProperty(ObjArray elems, Object property,
                                         bool isGetter)
        {
            Node f = function(FunctionNode.FUNCTION_EXPRESSION);
            if (f.getType() != Token.FUNCTION)
            {
                reportError("msg.bad.prop");
                return false;
            }
            int fnIndex = f.getExistingIntProp(Node.FUNCTION_PROP);
            FunctionNode fn = currentScriptOrFn.getFunctionNode(fnIndex);
            if (fn.getFunctionName().Length != 0)
            {
                reportError("msg.bad.prop");
                return false;
            }
            elems.add(property);
            if (isGetter)
            {
                elems.add(nf.createUnary(Token.GET, f));
            }
            else
            {
                elems.add(nf.createUnary(Token.SET, f));
            }
            return true;
        }

        public String getEncodedSource()
        {
            return encodedSource;
        }

        /// <summary>
        /// 销毁标记
        /// </summary>
        private void consumeToken()
        {
            currentFlaggedToken = Token.EOF;
        }
        /// <summary>
        /// 前取一个标记
        /// </summary>
        /// <returns></returns>
        private int peekToken()
        {
            int tt = currentFlaggedToken;
            if (tt == Token.EOF)
            {
                tt = ts.getToken();
                if (tt == Token.EOL)
                {
                    do
                    {
                        tt = ts.getToken();
                    } while (tt == Token.EOL);
                    tt |= TI_AFTER_EOL;
                }
                currentFlaggedToken = tt;
            }
            return tt & CLEAR_TI_MASK;
        }

        /// <summary>
        /// 创建编译器
        /// </summary>
        /// <returns></returns>
        protected Decompiler createDecompiler()
        {
            return new Decompiler();
        }
        public bool eof()
        {
            return ts.eof();

        }
        public void addError(String messageId)
        {
            ++syntaxErrorCount;
            String message = ScriptRuntime.getMessage0(messageId);
            errorReporter.error(message, sourceURI, ts.getLineno(),
                                ts.getLine(), ts.getOffset());
        }
        static int
        CLEAR_TI_MASK = 0xFFFF,   // mask to clear token information bits
        TI_AFTER_EOL = 1 << 16,  // first token of the source line
        TI_CHECK_LABEL = 1 << 17;  // indicates to check for label

        private ObjArray loopSet;
        private ObjArray loopAndSwitchSet;
        public bool calledByCompileFunction;
        private IDictionary<String, Node> labelSet;

        private int nestingOfWith;

        private int nestingOfFunction;

        private int syntaxErrorCount;
        private int currentFlaggedToken;
        public ScriptOrFnNode currentScriptOrFn;
        Node.Scope currentScope;

        private IRFactory nf;
        private Decompiler decompiler = null;
        private String encodedSource;

        public CompilerEnvirons compilerEnv = null;
        private ErrorReporter errorReporter = null;
        private String sourceURI = null;
        private TokenStream ts = null;
        private int endFlags;

    }
}
